package com.ilearn.iodata.iolingo.service.provider;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ilearn.iodata.iolingo.entities.ApiMetadata;
import com.ilearn.iodata.iolingo.mapper.ApiMetadataMapper;
import com.ilearn.iodata.iolingo.service.EdmTypeTrnsfService;
import com.ilearn.iodata.iolingo.service.metadata.api.GenericEdmMetadataPostProcessor;
import com.ilearn.iodata.iolingo.service.metadata.core.edm.mapper.api.GenericEntityType;
import com.ilearn.iodata.iolingo.service.metadata.core.edm.mapper.api.GenericServiceDocument;
import com.ilearn.iodata.iolingo.service.metadata.core.edm.mapper.impl.GenericEdmNameBuilder;
import com.ilearn.iodata.iolingo.service.metadata.core.edm.mapper.impl.GenericServiceDocumentFactory;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.provider.*;
import org.apache.olingo.commons.api.edmx.EdmxReference;
import org.apache.olingo.commons.api.ex.ODataException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

import static com.ilearn.iodata.iolingo.constant.OdataConst.CONTAINER_NAME;
import static com.ilearn.iodata.iolingo.constant.OdataConst.NAMESPACE;

@Service
public class GenericEdmProvider extends CsdlAbstractEdmProvider {


    private final GenericEdmNameBuilder nameBuilder;
    private final GenericServiceDocument serviceDocument;

    @Autowired
    private ApiMetadataMapper apiMetadataMapper;

    /*@Autowired
    EdmTypeTrnsfService edmTypeTrnsfService;*/

    private FullQualifiedName CONTAINER;

    public GenericEdmProvider(
    ) throws ODataException {
        super();
        CONTAINER = new FullQualifiedName(NAMESPACE, CONTAINER_NAME);
        this.nameBuilder = new GenericEdmNameBuilder(NAMESPACE);
        serviceDocument = new GenericServiceDocumentFactory(NAMESPACE)
                .getServiceDocument();
    }

    public void check() {
        System.out.println("np:" + NAMESPACE);
        System.out.println("ct:" + CONTAINER_NAME);
        System.out.println("fqn:" + CONTAINER);
        System.out.println("myFqn:" + new FullQualifiedName(NAMESPACE, CONTAINER_NAME));
    }

    public Map<String, List<ApiMetadata>> getMetadataFromDB() {
        Map<String, List<ApiMetadata>> map = new HashMap<String, List<ApiMetadata>>();

        //List<ApiMetadata> allApiMetadata = edmTypeTrnsfService.getAllApiMetadata();
        List<ApiMetadata> allApiMetadata = apiMetadataMapper.selectAll();

        for (ApiMetadata apiMetadatum : allApiMetadata) {
            if (!(map.containsKey(apiMetadatum.getApiName()))) {
                //System.out.println("apiName:" + apiMetadatum.getApiName());
                map.put(apiMetadatum.getApiName(), new ArrayList<>(Arrays.asList(apiMetadatum)));
            } else {
                List<ApiMetadata> apiMetadataList = map.get(apiMetadatum.getApiName());
                apiMetadataList.add(apiMetadatum);
                map.put(apiMetadatum.getApiName(), apiMetadataList);
            }
        }

        return map;
    }

    public Map<String, JSONObject> getMetadata() {
        String fileName = "D:\\iProject\\iLearnJava\\study-olingo-jdbc-test\\src\\main\\resources\\metadata.dat";
        File file = new File(fileName);
        BufferedReader reader = null;
        HashMap<String, JSONObject> map = new HashMap<String, JSONObject>();
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            // 一次读入一行，直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                JSONObject json = (JSONObject) JSON.parse(tempString);
                json.getJSONObject(tempString);
                System.out.println("json:" + json);
                map.put(json.getString("name"), json);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
        return map;
    }


    @Override
    public CsdlEnumType getEnumType(FullQualifiedName enumTypeName) throws ODataException {
        return super.getEnumType(enumTypeName);
    }

    @Override
    public CsdlTypeDefinition getTypeDefinition(FullQualifiedName typeDefinitionName) throws ODataException {
        return super.getTypeDefinition(typeDefinitionName);
    }

    @Override
    public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) throws ODataException {
        List<ApiMetadata> apiMetadataList = getMetadataFromDB().get(entityTypeName.getName());
        if (apiMetadataList != null) {
            //      //create EntityType properties
            List<CsdlProperty> csdlPropertys = new ArrayList<CsdlProperty>();
            List<CsdlPropertyRef> csdlPropertyRefs = new ArrayList<CsdlPropertyRef>();

            for (ApiMetadata apiMetadata : apiMetadataList) {

                CsdlProperty csdlProperty = new CsdlProperty();
                csdlProperty.setName(apiMetadata.getFieldAlias())
                        .setType(EdmPrimitiveTypeKind.valueOfFQN("Edm." + apiMetadata.getEdmType()).getFullQualifiedName());

                if (apiMetadata.getMaxLen() != null) {
                    csdlProperty.setMaxLength(apiMetadata.getMaxLen());
                }

                if (apiMetadata.getPrec() != null) {
                    csdlProperty.setPrecision(apiMetadata.getPrec());
                }

                if (apiMetadata.getScale() != null) {
                    csdlProperty.setScale(apiMetadata.getScale());
                }

                if (apiMetadata.getIsntUnicode() != null) {
                    csdlProperty.setUnicode(apiMetadata.getIsntUnicode());
                }

                if (apiMetadata.getNullable() != null) {
                    csdlProperty.setNullable(apiMetadata.getNullable());
                }

                if (apiMetadata.getIsntCollection() != null) {
                    csdlProperty.setCollection(apiMetadata.getIsntCollection());
                }

                if (apiMetadata.getDefaultValue() != null) {
                    csdlProperty.setDefaultValue(apiMetadata.getDefaultValue());
                }

                if (apiMetadata.getMimeType() != null) {
                    csdlProperty.setMimeType(apiMetadata.getMimeType());
                }

                csdlPropertys.add(csdlProperty);

                if (apiMetadata.getSyncMustType()) {
                    CsdlPropertyRef propertyRef = new CsdlPropertyRef();
                    propertyRef.setName(apiMetadata.getFieldAlias());
                    csdlPropertyRefs.add(propertyRef);
                }
            }

            // configure EntityType
            CsdlEntityType entityType = new CsdlEntityType();
            entityType.setName(entityTypeName.getName());
            entityType.setProperties(csdlPropertys);
            if (!csdlPropertyRefs.isEmpty()) {
                entityType.setKey(csdlPropertyRefs);
            }

            return entityType;
        }

        return null;
    }


    public CsdlEntityType getEntityTypeR(FullQualifiedName entityTypeName) throws ODataException {
        List<ApiMetadata> apiMetadataList = getMetadataFromDB().get(entityTypeName.getName());
        if (apiMetadataList != null) {
            //      //create EntityType properties
            List<GenericProperty> genericProperties = new ArrayList<GenericProperty>();
            List<CsdlPropertyRef> csdlPropertyRefs = new ArrayList<CsdlPropertyRef>();

            for (ApiMetadata apiMetadata : apiMetadataList) {

                GenericProperty genericProperty = new GenericProperty();
                genericProperty.setName(apiMetadata.getFieldAlias())
                        .setType(EdmPrimitiveTypeKind.valueOfFQN("Edm." + apiMetadata.getEdmType()).getFullQualifiedName());

                if (apiMetadata.getMaxLen() != null) {
                    genericProperty.setMaxLength(apiMetadata.getMaxLen());
                }

                if (apiMetadata.getPrec() != null) {
                    genericProperty.setPrecision(apiMetadata.getPrec());
                }

                if (apiMetadata.getScale() != null) {
                    genericProperty.setScale(apiMetadata.getScale());
                }

                if (apiMetadata.getIsntUnicode() != null) {
                    genericProperty.setUnicode(apiMetadata.getIsntUnicode());
                }

                if (apiMetadata.getNullable() != null) {
                    genericProperty.setNullable(apiMetadata.getNullable());
                }

                if (apiMetadata.getIsntCollection() != null) {
                    genericProperty.setCollection(apiMetadata.getIsntCollection());
                }

                if (apiMetadata.getDefaultValue() != null) {
                    genericProperty.setDefaultValue(apiMetadata.getDefaultValue());
                }

                if (apiMetadata.getMimeType() != null) {
                    genericProperty.setMimeType(apiMetadata.getMimeType());
                }

                genericProperties.add(genericProperty);

                if (apiMetadata.getSyncMustType()) {
                    CsdlPropertyRef propertyRef = new CsdlPropertyRef();
                    propertyRef.setName(apiMetadata.getFieldAlias());
                    csdlPropertyRefs.add(propertyRef);
                }
            }

            // configure EntityType
            CsdlEntityType entityType = new CsdlEntityType();
            entityType.setName(entityTypeName.getName());

            List<CsdlProperty> csdlProperties = new ArrayList<CsdlProperty>();
            csdlProperties.addAll(genericProperties);

            entityType.setProperties(csdlProperties);
            if (!csdlPropertyRefs.isEmpty()) {
                entityType.setKey(csdlPropertyRefs);
            }

            return entityType;
        }
        return null;
    }

    @Override
    public CsdlComplexType getComplexType(FullQualifiedName complexTypeName) throws ODataException {
        return super.getComplexType(complexTypeName);
    }

    @Override
    public List<CsdlAction> getActions(FullQualifiedName actionName) throws ODataException {
        return super.getActions(actionName);
    }

    @Override
    public List<CsdlFunction> getFunctions(FullQualifiedName functionName) throws ODataException {
        return super.getFunctions(functionName);
    }

    @Override
    public CsdlTerm getTerm(FullQualifiedName termName) throws ODataException {
        return super.getTerm(termName);
    }

    @Override
    public CsdlEntitySet getEntitySet(FullQualifiedName entityContainer, String entitySetName) throws ODataException {
        if (entityContainer.equals(CONTAINER)) {
            CsdlEntitySet entitySet = new CsdlEntitySet();
            entitySet.setName(entitySetName);
            entitySet.setType(new FullQualifiedName(NAMESPACE, entitySetName));
            return entitySet;
        }

        return null;
    }

    @Override
    public CsdlSingleton getSingleton(FullQualifiedName entityContainer, String singletonName) throws ODataException {
        return super.getSingleton(entityContainer, singletonName);
    }

    @Override
    public CsdlActionImport getActionImport(FullQualifiedName entityContainer, String actionImportName) throws ODataException {
        return super.getActionImport(entityContainer, actionImportName);
    }

    @Override
    public CsdlFunctionImport getFunctionImport(FullQualifiedName entityContainer, String functionImportName) throws ODataException {
        return super.getFunctionImport(entityContainer, functionImportName);
    }

    @Override
    public CsdlEntityContainerInfo getEntityContainerInfo(FullQualifiedName entityContainerName) throws ODataException {
        // This method is invoked when displaying the service document at
        // e.g. http://localhost:8080/DemoService/DemoService.svc
        if (entityContainerName == null || entityContainerName.equals(CONTAINER)) {
            CsdlEntityContainerInfo entityContainerInfo = new CsdlEntityContainerInfo();
            entityContainerInfo.setContainerName(CONTAINER);
            return entityContainerInfo;
        }
        return null;
    }

    @Override
    public List<CsdlAliasInfo> getAliasInfos() throws ODataException {
        return super.getAliasInfos();
    }

    @Override
    public List<CsdlSchema> getSchemas() throws ODataException {
        // create Schema
        CsdlSchema schema = new CsdlSchema();
        schema.setNamespace(NAMESPACE);

        // add EntityTypes
        List<CsdlEntityType> entityTypes = new ArrayList<CsdlEntityType>();

        //for (String apiName : getMetadata().keySet()) {
        for (String apiName : getMetadataFromDB().keySet()) {
            entityTypes.add(getEntityType(new FullQualifiedName(NAMESPACE, apiName)));
        }
        schema.setEntityTypes(entityTypes);

        // add EntityContainer
        schema.setEntityContainer(getEntityContainer());

        // finally
        List<CsdlSchema> schemas = new ArrayList<CsdlSchema>();
        schemas.add(schema);

        return schemas;
    }

    @Override
    public CsdlEntityContainer getEntityContainer() throws ODataException {
        // create EntitySets
        List<CsdlEntitySet> entitySets = new ArrayList<CsdlEntitySet>();

        //for (String apiName : getMetadata().keySet()) {
        for (String apiName : getMetadataFromDB().keySet()) {
            entitySets.add(getEntitySet(CONTAINER, apiName));
        }

        // create EntityContainer
        CsdlEntityContainer entityContainer = new CsdlEntityContainer();
        entityContainer.setName(CONTAINER_NAME);
        entityContainer.setEntitySets(entitySets);

        return entityContainer;
    }

    @Override
    public CsdlAnnotations getAnnotationsGroup(FullQualifiedName targetName, String qualifier) throws ODataException {
        return super.getAnnotationsGroup(targetName, qualifier);
    }

    public GenericServiceDocument getServiceDocument() {
        return serviceDocument;
    }


    public List<EdmxReference> getReferences() {
        try {
            return serviceDocument.getReferences();
        } catch (Exception e) {
            return new ArrayList<EdmxReference>();
        }
    }
}
